unit frmmain;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, vg_layouts, vg_memo, vg_scene, vg_controls, vg_textbox,vm,
  cfthread,zeromq,vg_listbox,iniFiles;

type
  Tfrmconsole = class(TForm, IListener)
    vgScene1: TvgScene;
    Root1: TvgBackground;
    memoconsole: TvgMemo;
    Layout1: TvgLayout;
    Layout2: TvgLayout;
    txtcmd: TvgTextBox;
    btnlocalexec: TvgButton;
    txtremote: TvgTextBox;
    btnremoteexec: TvgButton;
    cbonline: TvgCheckBox;
    cbConnect: TvgCheckBox;
    StringListBox1: TvgStringListBox;
    Splitter1: TvgSplitter;
    txtmyname: TvgTextBox;
    txthost: TvgTextBox;
    procedure FormCreate(Sender: TObject);
    procedure btnlocalexecClick(Sender: TObject);
    procedure cbonlineChange(Sender: TObject);
    procedure btnremoteexecClick(Sender: TObject);
    procedure cbConnectChange(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure txtcmdKeyUp(var Key: Word; var KeyChar: WideChar;
      Shift: TShiftState);
    procedure txthostChange(Sender: TObject);
  private
    { Private declarations }
  public
    engine:TKsanaForthVM;
    outputbuf:string;
    cloudforth: TCloudForthThread;
    remote_ex:integer;
    { Public declarations }
     procedure FromRemote(const AMessage: AnsiString);
  end;

var
  frmconsole: Tfrmconsole;
  ini:tIniFile;
  exePath:string;
  host:string;
implementation

procedure iniwriteinteger(sec,itm:string;val:integer);
var i:integer;
begin
  i:=ini.readinteger(sec,itm,-1);
  if i<>val then
     ini.writeinteger(sec,itm,val);
end;

procedure iniwritestring(sec,itm:string;val:string);
begin
  if ini.readstring(sec,itm,'')<>val then
     ini.writestring(sec,itm,val);
end;

procedure consoleztype(vm:Tksanaforthvm;s:pchar);
begin
  frmconsole.outputbuf:=frmconsole.outputbuf+s;
end;
procedure consoleinputcheck(vm:Tksanaforthvm);
begin
  vm.tos:=vm.tos;
end;

procedure flush;
begin
  frmconsole.memoconsole.lines.add(frmconsole.outputbuf);
  frmconsole.outputbuf:='';
end;
procedure consoleemit(vm:Tksanaforthvm;c:char);
begin
  if c=#13 then begin
    flush;
  end else begin
    frmconsole.outputbuf:=frmconsole.outputbuf+c;
  end;
end;

{$R *.dfm}

// send tos as string to remote 
procedure remote_exec(vm:TksanaForthVM);
var
  s:string;
begin
  s:=pchar(vm.pop);
  frmconsole.cloudforth.zmq.Send(
  frmconsole.cloudforth.ex_cmd,'<'+frmconsole.txtmyname.text
  +#13#10+s,zmqNoBlocking);
  frmconsole.memoconsole.lines.add('RUN on '+frmconsole.txtremote.text+#13#10+s);
//frmconsole.memoconsole.lines.add('Remote< '+s);
end;
procedure remote_handler (vm:TKsanaForthVM);
begin
  vm.tickztype(vm,pchar(vm.ptib));
  flush;
  vm.ptib:=pchar(vm.tib);
  pchar(vm.ptib)^:=#0;
end;

procedure Tfrmconsole.FormDestroy(Sender: TObject);
begin
  iniwriteInteger('setting','listwidth',trunc(stringlistbox1.width));
  ini.Free;
end;

procedure Tfrmconsole.FormCreate(Sender: TObject);
var exeName:string;
begin
  exeName:=application.exename;
  caption:=exeName;
  exePath:=extractFilePath(exeName);
  exeName:=extractFileName(exeName);
  exeName:=copy(exeName,1,length(exeName)-4);
  ini:=tIniFile.Create(exePath+exeName+'.ini');
  txtmyname.Text:=widestring(ini.ReadString('setting','myname','sam'));
  txtremote.Text:=widestring(ini.ReadString('setting','remote','yap'));
  stringlistbox1.width:=ini.ReadInteger('setting','listwidth',100);
  host:=widestring(ini.ReadString('setting','host','www.ksana.tw'));
  txthost.Text:=host;
  engine:=TKsanaForthVM.create;
  engine.interactive:=true;
  engine.tickinputcheck := consoleinputcheck;
  engine.tickztype := consoleztype;
  engine.tickemit := consoleemit;
  engine.addcodeword('remote_handler'  ,integer(@remote_handler)); //// ( -- )
  engine.addcodeword('remote_exec'  ,integer(@remote_exec)); //// ( a -- ) zero-ended string
  engine.fence:=engine.words.count;
  engine.reset;
  engine.preload;

end;

procedure Tfrmconsole.btnlocalexecClick(Sender: TObject);
begin
  stringlistbox1.items.Insert(0,txtcmd.Text);
  stringlistbox1.itemindex:=0;
  memoconsole.lines.add('Local< '+txtcmd.text);
  engine.eval(txtcmd.text);
  txtcmd.text:='';
  flush;
end;

procedure Tfrmconsole.cbonlineChange(Sender: TObject);
begin
  if cbonline.IsChecked then begin
    cbconnect.Enabled:=true;
    cloudforth := TCloudForthThread.Create(true);
    cloudforth.host:=host;
    cloudforth.myname:=txtmyname.text;
    cloudforth.Listener := self;
    cloudforth.Resume;
    iniWriteString('setting','myname',string(txtmyname.Text));
  end else begin
    if assigned(cloudforth) then begin
      cloudforth.Terminate;
      cloudforth.WaitFor;
      cloudforth.Free;
      cloudforth := nil;
      cbconnect.Enabled:=false;      
    end;
  end;
end;

// first line: >remote_id
// second line: command

// first line: <remote_id
// second line: simply display

procedure Tfrmconsole.FromRemote(const AMessage: AnsiString);
var
  sl:tstringlist;
  from:string;
  cmd:string;
begin
  sl:=tstringlist.create;
  sl.Text:=amessage;
  if sl.count<2 then exit;
  from:=sl[0];
  sl.delete(0);
  cmd:=sl.text;
  engine.interactive:=false;

  if copy(from,1,1)='<' then begin
    engine.tickztype(engine,pchar('FROM '+from+'==> '+pchar(cmd)));
    flush;
    engine.eval(cmd); //do not show prompt
    cloudforth.forthresult:=outputbuf;
    flush;
  end else begin
    engine.eval( 'remote_handler '+from+' '+cmd);
//    memoconsole.lines.add('RETURN FROM '+from);
//    memoconsole.lines.add(cmd); // remaining text is the result from remote forth
  end;
  sl.free;

  engine.interactive:=true;  
end;

procedure Tfrmconsole.btnremoteexecClick(Sender: TObject);
begin
  stringlistbox1.items.Insert(0,txtcmd.Text);
  stringlistbox1.itemindex:=0;
  cloudforth.zmq.Send(cloudforth.ex_cmd,'<'+txtmyname.text+#13#10+txtcmd.text,zmqNoBlocking);
  memoconsole.lines.add('RUN on '+txtremote.text+#13#10+txtcmd.text);
  txtcmd.text:='';
end;

procedure Tfrmconsole.cbConnectChange(Sender: TObject);
begin
  if cbconnect.IsChecked then begin
    cloudforth.ex_cmd:= cloudforth.zmq.CreateLocalExchange('LocalE', zmqStyleDataDistribution);
    cloudforth.zmq.Bind('LocalE','Q_'+txtremote.text);

    cloudforth.zmq.CreateLocalQueue('LocalQ');
    cloudforth.zmq.Bind('E_'+txtremote.text,'LocalQ');

    btnremoteexec.Enabled:=true;
    iniWriteString('setting','remote',string(txtremote.Text));
  end else begin
    btnremoteexec.Enabled:=false;
  end;
end;

procedure Tfrmconsole.txtcmdKeyUp(var Key: Word; var KeyChar: WideChar;
  Shift: TShiftState);
begin
  if key=13 then btnlocalexecClick(nil)
  else if key=38 then begin
     txtcmd.Text:=stringlistbox1.items[stringlistbox1.itemindex];
     if stringlistbox1.itemindex+1<stringlistbox1.items.count then
        stringlistbox1.itemindex:=stringlistbox1.itemindex+1;
  end;
end;

procedure Tfrmconsole.txthostChange(Sender: TObject);
begin
  host:=txthost.Text;
  iniwritestring('setting','host',host);
end;

end.
